/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLScatter.h>
#include <pgl/core/PGLDataVectors.h>
#include <pgl/core/DataRegistry.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif



PGL_IMPLEMENT_DYNCREATE(CPGLScatter, CPGLObject);
PGL_IMPLEMENT_SCRIPTING(Scatter);

//! Save settings
void CPGLScatter::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLScatter");

	CString datas, str, strPos;
	std::_tstring strX,strY;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();
		
			// base class
			CPGLObject::SerializeXML(ar);

			xml.AddChildElemEx(_T("MaxValue"),m_dMaxValue);
			xml.AddChildElemEx(_T("MinValue"),m_dMinValue);
			xml.AddChildElem(_T("ColorMap"),CPGLUtility::ColorMap2String(m_eColorMap));
			xml.AddChildElemEx(_T("MarkerWidth"), m_dMarkerWidth);
			xml.AddChildElem(_T("MarkerType"),MarkerType2String(m_eMarkerType));
			if (m_eMarkerType == MarkerTypeText)
			{
				xml.AddChildElemEx(_T("MarkerText"));
				xml.IntoElem();
					m_textMarker.SerializeXML(ar);
				xml.OutOfElem();
			}
			xml.AddChildElem(_T("Data"));
			xml.IntoElem();
				xml.AddChildElemEx(_T("Flushing"),m_bDataFlushing);
				if (m_pX)
				{
					xml.AddChildElem(_T("Xs"));
					xml.IntoElem();
						m_pX->SerializeXML(ar);
					xml.OutOfElem();
				}
				if (m_pY)
				{
					xml.AddChildElem(_T("Ys"));
					xml.IntoElem();
						m_pX->SerializeXML(ar);
					xml.OutOfElem();
				}
				if (m_pValues)
				{
					xml.AddChildElem(_T("Values"));
					xml.IntoElem();
						m_pValues->SerializeXML(ar);
					xml.OutOfElem();
				}
			xml.OutOfElem();

		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLObject::SerializeXML(ar);

			xml.FindGetChildData(_T("MaxValue"),m_dMaxValue);
			xml.FindGetChildData(_T("MinValue"),m_dMinValue);
			xml.FindGetChildData(_T("ColorMap"),str);
			m_eColorMap = CPGLUtility::String2ColorMap(str);
			xml.FindGetChildData(_T("MarkerWidth"), m_dMarkerWidth);
			xml.FindGetChildData(_T("MarkerType"),str);
			m_eMarkerType = String2MarkerType(str);
			if (m_eMarkerType == MarkerTypeText)
			{
				xml.ResetChildPos();
				if(xml.FindChildElem(_T("MarkerText")))
				{
					xml.IntoElem();
						m_textMarker.SerializeXML(ar);
					xml.OutOfElem();
				}
			}

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("Data")))
			{
				xml.IntoElem();
					xml.FindGetChildData(_T("Flushing"),m_bDataFlushing);
					PGL_SERIALIZE_LOAD_DATA( strPos , Xs , m_pX );
					PGL_SERIALIZE_LOAD_DATA( strPos , Ys , m_pY );
					PGL_SERIALIZE_LOAD_DATA( strPos , Values , m_pValues );

				xml.OutOfElem();
			}
		xml.OutOfElem();

		PostUpdateExtent();
	}
}

#ifdef _DEBUG
void CPGLScatter::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLObject::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLScatter ID ") << GetID() << "\n";
}
void CPGLScatter::AssertValid() const
{
    // call inherited AssertValid first
    CPGLObject::AssertValid();
} 
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPGLScatter::CPGLScatter()
: m_eMarkerType( MarkerTypeSquare), m_dMarkerWidth(10), m_dMinValue(0), m_dMaxValue(1),
m_eColorMap( CPGLUtility::ColorMapHsv), m_bDataFlushing(false)
{
	m_textMarker.SetString(_T("+"));

	PreparePropertyMap();
}

CPGLScatter::~CPGLScatter()
{
	CleanData();
}

size_t CPGLScatter::GetNPoints() const
{
	if (!m_pX || !m_pY || !m_pValues)
		return 0;
	size_t nx=m_pX->size();
	size_t ny=m_pY->size();
	size_t nv=m_pValues->size();

	return __min( nx, __min( ny, nv) );
}

void CPGLScatter::CleanData()
{
	m_pX.reset();
	m_pY.reset();
	m_pValues.reset();
}

CPGLScatter& CPGLScatter::operator =(const CPGLScatter& s)
{
	if(&s != this)
	{
		this->CPGLObject::operator =(s);

		CleanData();

		if (s.m_pX)
			m_pX = s.m_pX->Clone();
		if (s.m_pY)
			m_pY = s.m_pY->Clone();
		if (s.m_pValues)
			m_pValues = s.m_pValues->Clone();

		m_bDataFlushing = s.m_bDataFlushing;
		m_dMinValue = s.m_dMinValue;
		m_dMaxValue = s.m_dMaxValue;
		m_eMarkerType = s.m_eMarkerType;
		m_textMarker = s.m_textMarker;
		m_dMarkerWidth = s.m_dMarkerWidth;
		m_eColorMap = s.m_eColorMap;
	}
	return *this;
}

	//! Extent computation
void CPGLScatter::GetUnionExtent(gfxinterface::CGfxInterface& gfx, CPGLRectD& rExtent)
{
	CPGLObject::GetUnionExtent(gfx,rExtent);

	GetExtent().SetUnion(rExtent);

	rExtent.xValid = true;
	rExtent.yValid = true;
}
    ///virtual function to override in derived classes
void CPGLScatter::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	CPGLObject::UpdateExtent(gfx);

	m_rLimitIndices=CPGLRectU();

	UINT nPoints=GetNPoints();
	// updating...
	CPGLRectD rExtent;

	if (!m_pX || !m_pY || !m_pValues || !nPoints)
	{
		rExtent.xValid=rExtent.yValid=false;
		SetExtent( CPGLRectD::GetNormBox() );
		return;
	}

	if (m_pX)
	{
		m_pX->GetMinMaxIndex(m_rLimitIndices.left, m_rLimitIndices.right);
		rExtent.left = (*m_pX)[m_rLimitIndices.left];
		rExtent.right = (*m_pX)[m_rLimitIndices.right];
	}

	if (m_pY)
	{
		m_pY->GetMinMaxIndex(m_rLimitIndices.bottom, m_rLimitIndices.top);
		rExtent.bottom = (*m_pY)[m_rLimitIndices.bottom];
		rExtent.top = (*m_pY)[m_rLimitIndices.top];
	}	

	// setting extent
	SetExtent(rExtent);
}


void CPGLScatter::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	using namespace gfxinterface;
	// not visible
	if(!IsVisible())
		return;

	if (!IsInLayer(gfx.GetLayer()))
		return;

	// no points
	const size_t nPoints=GetNPoints();
	if (nPoints==0)
		return;

	double phase;
	size_t i;
	CPGLColor col;
	CPGLVectorF vx,vy;
	double fact = 1/(m_dMaxValue - m_dMinValue);
	// gettin size of character...
	SPointD p;
	double dy;

	ASSERT(m_pX);
	ASSERT(m_pY);
	ASSERT(m_pValues);
	switch(m_eMarkerType)
	{
	case MarkerTypeSquare:
		vx.resize(4); 
		vy.resize(4);
		vx[0]=static_cast<float>(-m_dMarkerWidth/2.0); vy[0]=static_cast<float>(-m_dMarkerWidth/2.0);
		vx[1]=static_cast<float>(m_dMarkerWidth/2.0); vy[1]=static_cast<float>(-m_dMarkerWidth/2.0);
		vx[2]=static_cast<float>(m_dMarkerWidth/2.0); vy[2]=static_cast<float>(m_dMarkerWidth/2.0);
		vx[3]=static_cast<float>(-m_dMarkerWidth/2.0); vy[3]=static_cast<float>(m_dMarkerWidth/2.0);

		for (i=0;i<nPoints;i++)
		{
			CPGLUtility::ColorMap( m_eColorMap, ((*m_pValues)[i] - m_dMinValue)*fact, col);
			gfx.SetFillColor(col.GetRed(), col.GetGreen(), col.GetBlue(), col.GetAlpha());
			gfx.DrawPolygon((*m_pX)[i],(*m_pY)[i], vx,vy, LineTypeFilled | LineTypeNoContour);
		}
		break;
	case MarkerTypeCircle:
		for (i=0;i<nPoints;i++)
		{
			CPGLUtility::ColorMap( m_eColorMap, ((*m_pValues)[i] - m_dMinValue)*fact, col);
			gfx.SetFillColor(col.GetRed(), col.GetGreen(), col.GetBlue(), col.GetAlpha());
			gfx.DrawCircle((*m_pX)[i],(*m_pY)[i], m_dMarkerWidth/2.0, LineTypeFilled | LineTypeNoContour);
		}
		break;
	case MarkerTypeTriangle:
		vx.resize(3); 
		vy.resize(3);
		phase=-PGL_PI/6.0;
		vx[0]=(float)(m_dMarkerWidth*cos(phase));				vy[0]=(float)(m_dMarkerWidth*sin(phase));
		vx[1]=(float)(m_dMarkerWidth*cos(phase+2*PGL_PI/3));	vy[1]=(float)(m_dMarkerWidth*sin(phase+2*PGL_PI/3));
		vx[2]=(float)(m_dMarkerWidth*cos(phase+2*PGL_PI/3*2));	vy[2]=(float)(m_dMarkerWidth*sin(phase+2*PGL_PI/3*2));

		for (i=0;i<nPoints;i++)
		{
			CPGLUtility::ColorMap( m_eColorMap, ((*m_pValues)[i] - m_dMinValue)*fact, col);
			gfx.SetFillColor(col.GetRed(), col.GetGreen(), col.GetBlue(), col.GetAlpha());
			gfx.DrawPolygon((*m_pX)[i],(*m_pY)[i], vx,vy , LineTypeFilled | LineTypeNoContour);
		}
		break;
	case MarkerTypeText:
		p=gfx.MeasureString(m_textMarker.GetString(),m_textMarker.GetFont().GetLOGFONT());
		dy=gfx.BBToWorldAbsy(p.y*0.5);
		// setting font
		m_textMarker.GetFont().SetFontGfx(gfx);
		for (i=0;i<nPoints;i++)
		{
			CPGLUtility::ColorMap( m_eColorMap, ((*m_pValues)[i] - m_dMinValue)*fact, col);
			gfx.SetColor(col.GetRed(), col.GetGreen(), col.GetBlue(), col.GetAlpha());
			gfx.DrawText((*m_pX)[i],(*m_pY)[i]-dy, m_textMarker.GetString(), TextAlignmentCenter, m_textMarker.GetAngle());
		}

		break;
	}

	if (m_bDataFlushing)
	{
		m_pX->clear();
		m_pY->clear();
		m_pValues->clear();
	}
}

void CPGLScatter::SetMarkerWidth(double dMarkerWidth)
{	
	m_dMarkerWidth = dMarkerWidth;
	LOGFONT lf=m_textMarker.GetFont().GetLOGFONT();
	lf.lfHeight = (LONG)floor(dMarkerWidth);
	m_textMarker.GetFont().SetLOGFONT(lf);
};

void CPGLScatter::PlotLegendGfx(gfxinterface::CGfxInterface& gfx, const CPGLRectD& rBox)
{
/*	if (!IsVisible())
		return;


	// calling base class plot
	CPGLObject::PlotGfx(gfx);

	gfx.SetLineWidth(m_fLineWidth);
	gfx.PushState();
		DWORD lineType = GetELineType();
		SetDashGfx(gfx);
		CPGLVectorF vX;
		CPGLVectorF vY;
		if ( (!IsOpen() && ! IsSnapBottom())
			|| (IsFilled() && ! IsSnapBottom()) )
		{
			vX.resize(4);
			vY.resize(4);
			vX[0]=(float)rBox.left; vX[1]=(float)rBox.right;
			vX[2]=(float)rBox.right; vX[3]=(float)rBox.left;
			vY[0]=vY[1]=(float)(rBox.bottom+rBox.GetHeight());
			vY[2]=vY[3]=(float)rBox.bottom;
			pBrush->SetBBox( rBox );
			pBrush->MakeCurrentFill(gfx);
			gfx.DrawLineStrip(vX, vY, lineType);
		}
		else
		{
			vX.resize(2);
			vY.resize(2);
			vX[0]=(float)rBox.left; vX[1]=(float)rBox.right;
			vY[0]=vY[1]=(float)(rBox.bottom+rBox.GetHeight()*2/3.0);
			pBrush->SetBBox( CPGLRectD(rBox.left, rBox.bottom, rBox.right, vY[1]) );
			pBrush->MakeCurrentFill(gfx);
			gfx.DrawLineStrip(vX, vY, lineType);
		}
	gfx.PopState();

	ASSERT(m_pMarker);
	gfx.PushState();
		if (m_pMarker->IsVisible())
		{
			double x=rBox.left+rBox.GetWidth()/2.0;
			double y=rBox.bottom+rBox.GetHeight()/2.0;

			m_pMarker->PlotLegendGfx(gfx,(float)x,(float)y);
		}
	gfx.PopState();

	delete pBrush;
*/
}

void CPGLScatter::SetDatas(CPGLDataPtr pX, CPGLDataPtr pY, CPGLDataPtr pValues)
{
	CleanData();

	m_pX=pX;
	m_pY=pY;
	m_pValues=pValues;
	PostUpdateExtent();
}

//! Sets the datas
void CPGLScatter::SetDatas(const std::vector<float>& vXs, const std::vector<float>& vYs, const std::vector<float>& vValues)
{
	SetDatas( CPGLVectorFPtr(new CPGLVectorF(vXs)), CPGLVectorFPtr(new CPGLVectorF(vYs)), CPGLVectorFPtr(new CPGLVectorF(vValues)));
}

LPCTSTR CPGLScatter::MarkerType2String(EMarkerType type)
{
	switch(type)
	{
	case MarkerTypeSquare:
		return _T("square");
	case MarkerTypeTriangle:
		return _T("tri");
	case MarkerTypeText:
		return _T("text");
	default:
		return _T("circle");
	}
}

CPGLScatter::EMarkerType CPGLScatter::String2MarkerType(LPCTSTR strType)
{
	CString str = strType;
	if (str==_T("tri"))
		return MarkerTypeTriangle;
	else if (str==_T("square"))
		return MarkerTypeSquare;
	else if (str==_T("text"))
		return MarkerTypeText;
	else
		return MarkerTypeCircle;
}

void CPGLScatter::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);

	if (bLoaded)
		return;
	bLoaded=true;

	CPGLObject::PreparePropertyMap();
	
	m_mProps[_T("Scatter.markertype")]=PropertyPair(ScatterPropMarkerType, _T("circle, tri, square, text"));
	m_mProps[_T("Scatter.markerwidth")]=PropertyPair(ScatterPropMarkerWidth, _T("width in pixels"));
	m_mProps[_T("Scatter.markerstring")]=PropertyPair(ScatterPropMarkerString, _T("string maker, applies to marker text type"));
	m_mProps[_T("Scatter.colormap")]=PropertyPair(ScatterPropColorMap, _T("HSV, Jet, Gray, Pink, Cool, Bone, Copper, Hot, Autumn, Winter, Spring, Summer"));
	m_mProps[_T("Scatter.dataflusing")]=PropertyPair(ScatterPropDataFlushing, _T("on, true - off, false"));
	m_mProps[_T("Scatter.minvalue")]=PropertyPair(ScatterPropMinValue, _T("Minimum value, lower values are truncated"));
	m_mProps[_T("Scatter.maxvalue")]=PropertyPair(ScatterPropMaxValue, _T("Maximum value, higher values are truncated"));
}

bool CPGLScatter::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	if (CPGLObject::SetProperty(szProp, szValue))
		return true;

	CString sv(szValue);
	sv.MakeLower();

	UINT u=0;
	float f=0;
	switch( StringToScatterProp( szProp ))
	{
	case ScatterPropMarkerType:
		SetMarkerType( String2MarkerType( sv ) );
		return true;
	case ScatterPropMarkerWidth:
		if (_stscanf(sv,_T("%f"),&f) == 1)
			SetMarkerWidth(f);
		return true;
	case ScatterPropMarkerString:
		SetString( szValue );
		return true;
	case ScatterPropColorMap:
		SetColorMap( CPGLUtility::String2ColorMap( szValue ) );
		return true;
	case ScatterPropDataFlushing:
		SetDataFlushing( sv==_T("on") || sv==_T("true"));
		return true;
	case ScatterPropMinValue:
		if (_stscanf(sv,_T("%f"),&f) == 1)
			SetMinValue(f);
		return true;
	case ScatterPropMaxValue:
		if (_stscanf(sv,_T("%f"),&f) == 1)
			SetMaxValue(f);
		return true;
	default:
		return false;
	};
}

bool CPGLScatter::Get( LPCTSTR szProp, CString& str)
{
	if(CPGLObject::Get(szProp, str))
		return true;

	switch( StringToScatterProp( szProp ))
	{
	case ScatterPropMarkerType:
		str=MarkerType2String( GetMarkerType());
		return true;
	case ScatterPropMarkerWidth:
		str.Format(_T("%g"), GetMarkerWidth());
		return true;
	case ScatterPropMarkerString:
		str=GetString();
		return true;
	case ScatterPropColorMap:
		str=CPGLUtility::ColorMap2String( GetColorMap() );
		return true;
	case ScatterPropDataFlushing:
		str=(IsDataFlushing()) ? _T("true") : _T("false");
		return true;
	case ScatterPropMinValue:
		str.Format(_T("%g"), GetMinValue());
		return true;
	case ScatterPropMaxValue:
		str.Format(_T("%g"), GetMaxValue());
		return true;
	default:
		return false;
	};
}
